package cc.mcyx.ychat.service.impl

import cc.mcyx.ychat.execution.MessageException
import cc.mcyx.ychat.model.User
import cc.mcyx.ychat.service.UserService
import cc.mcyx.ychat.vo.request.LoginRequestVo
import cc.mcyx.ychat.vo.response.LoginResponseVo
import cn.hutool.core.date.DateField
import cn.hutool.core.date.DateUtil
import cn.hutool.jwt.JWT
import cn.hutool.jwt.JWTUtil
import cn.hutool.jwt.signers.JWTSigner
import cn.hutool.jwt.signers.JWTSignerUtil
import com.mybatisflex.core.query.QueryWrapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import util.md5Str


@Service
open class AuthService {
    private val jwtSigner: JWTSigner = JWTSignerUtil.hs256("qwq".toByteArray(Charsets.UTF_8))

    @Autowired
    lateinit var redisService: RedisService

    @Autowired
    lateinit var userService: UserService

    fun login(loginRequestVo: LoginRequestVo): LoginResponseVo {
        val one = userService.getOne(
            QueryWrapper.create().eq("username", loginRequestVo.username).eq("password", loginRequestVo.password)
        )
        if (one == null) throw MessageException("账号或者密码错误", 401)
        return LoginResponseVo(createToken(one))
    }

    fun checkTokenEx(token: String) {
        try {
            val verify = JWTUtil.verify(token, jwtSigner)
            if (verify.not()) throw RuntimeException()
            val ps = JWT.of(token).getPayload("sign")
            if (ps != md5Str(
                    userService.getById(
                        tokenToUid(
                            token,
                            false
                        )
                    ).password
                )
            ) throw MessageException("Token 失效", 401)
        } catch (t: Throwable) {
            t.printStackTrace()
            throw MessageException(t.message ?: "Token is invalid", 401)
        }

    }

    private fun createToken(user: User): String {
        val now = DateUtil.date()
        val jwt = JWT.create()
        jwt.setPayload("uid", user.id)
        jwt.setPayload("sign", md5Str(user.password))
        jwt.setIssuedAt(now)
        jwt.setExpiresAt(now.offset(DateField.HOUR_OF_DAY, 1))
        jwt.setSubject("Login")
        return jwt.sign(jwtSigner).also { redisService.setData(it, user.id) }
    }

    /**
     * 从缓存获取记录的用户token
     * @return 返回用户uid
     */
    fun getCacheIdByToken(token: String): Int {
        return redisService.getData(token).toString().toIntOrNull() ?: -1
    }


    fun tokenToUid(token: String, check: Boolean = true): Int {
        if (check) this.checkTokenEx(token)
        val uid: Int = JWT.of(token).getPayload("uid").toString().toInt()
        return uid
    }

}